﻿#region Using Statements
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using starLiGHT;

#endregion

#region COPYRIGHT

/*
    Copyright (c) 2008, 2009, 2010, 2011 
        Roland Rosenkranz (Glatzemann@email.de)
*/

#endregion

#region LICENSE

/*
    This file is part of starLiGHT.Collision.

    starLiGHT.Collision is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    starLiGHT.Collision is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with starLiGHT.Collision.  If not, see <http://www.gnu.org/licenses/>.

    
    ADDITIONAL (commercial) LICENSES for starLiGHT.Collision are available on request.
*/

#endregion

#region Box2D Copyright
/*
* Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#endregion

#region Version Stuff
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 1486                  $:  Revision of last commit *
// * $Author:: glatzemann         $:  Author of last commit   *
// * $Date:: 2010-10-08 15:55:47 #$:  Date of last commit     *
// ************************************************************

// **************[ Box2D-Repository Info ]*********************
// Header-File: http://code.google.com/p/box2d/source/browse/trunk/Box2D/Box2D/Dynamics/Joints/b2Joint.h
// Revision   : r141
// Change-Date: 2010-10-10
//
// Source-File: http://code.google.com/p/box2d/source/browse/trunk/Box2D/Box2D/Dynamics/Joints/b2Joint.cpp
// Revision   : r141
// Change-Date: 2010-10-10
//
// Status     : Finished!
#endregion

namespace starLiGHT.Physic.TwoD
{
    public enum JointType
    {
	    UNKNOWN,
	    REVOLUTE,
	    PRISMATIC,
	    DISTANCE,
	    PULLEY,
	    MOUSE,
	    GEAR,
	    LINE,
        WELD,
        FRICTION,
        ROPE,
    };

    public enum LimitState
    {
	    INACTIVE,
	    AT_LOWER,
	    AT_UPPER,
	    EQUAL
    };

    public struct Jacobian
    {
	    public Vector2 linear1;
        public float angular1;
        public Vector2 linear2;
        public float angular2;

        public void SetZero()
        {
            linear1 = Vector2.Zero; angular1 = 0.0f;
            linear2 = Vector2.Zero; angular2 = 0.0f;
        }

        public void Set(Vector2 x1, float a1, Vector2 x2, float a2)
        {
            linear1 = x1; angular1 = a1;
            linear2 = x2; angular2 = a2;
        }

        public float Compute(Vector2 x1, float a1, Vector2 x2, float a2)
        {
            return Vector2.Dot(linear1, x1) + angular1 * a1 + Vector2.Dot(linear2, x2) + angular2 * a2;
        }
    };

    /// <summary>
    /// A joint edge is used to connect bodies and joints together
    /// in a joint graph where each body is a node and each joint
    /// is an edge. A joint edge belongs to a doubly linked list
    /// maintained in each attached body. Each joint has two joint
    /// nodes, one for each attached body.
    /// </summary>
    public class JointEdge
    {
        public Body other;			// provides quick access to the other body attached.
        public Joint joint;			// the joint
        public JointEdge prev;		    // the previous joint edge in the body's joint list
        public JointEdge next;		    // the next joint edge in the body's joint list
    };

    /// <summary>Joint definitions are used to construct joints.</summary>
    public class JointDef
    {
        public JointDef()
        {
            type = JointType.UNKNOWN;
            userData = null;
            body1 = null;
            body2 = null;
            collideConnected = false;
        }

        /// <summary>The joint type is set automatically for concrete joint types.</summary>
        public JointType type;

        /// <summary>Use this to attach application specific data to your joints.</summary>
        public object userData;

        /// <summary>The first attached body.</summary>
        public Body body1;

        /// <summary>The second attached body.</summary>
        public Body body2;

        /// <summary>Set this flag to true if the attached bodies should collide.</summary>
        public bool collideConnected;
    };

    /// <summary>The base joint class. Joints are used to constraint two bodies together in
    /// various fashions. Some joints also feature limits and motors.</summary>
    public abstract class Joint
    {
        /// <summary>Get the type of the concrete joint.</summary>
        new public JointType GetType()
        {
            return m_type;
        }

        /// <summary>Get the first body attached to this joint.</summary>
        public Body GetBodyA()
        {
            return m_body1;
        }

        /// <summary>Get the second body attached to this joint.</summary>
        public Body GetBodyB()
        {
            return m_body2;
        }

        /// <summary>Get the anchor point on body1 in world coordinates.</summary>
	    public abstract Vector2 GetAnchorA();

        /// <summary>Get the anchor point on body2 in world coordinates.</summary>
	    public abstract Vector2 GetAnchorB() ;

        /// <summary>Get the reaction force on body2 at the joint anchor in Newtons.</summary>
	    public abstract Vector2 GetReactionForce(float inv_dt);

        /// <summary>Get the reaction torque on body2 in N*m.</summary>
	    public abstract float GetReactionTorque(float inv_dt);

        /// <summary>Get the next joint the world joint list.</summary>
        public Joint GetNext()
        {
            return m_next;
        }

        /// <summary>Get the user data pointer.</summary>
        public object GetUserData()
        {
            return m_userData;
        }

        /// <summary>Set the user data pointer.</summary>
        public void SetUserData(object data)
        {
            m_userData = data;
        }

        public bool IsActive()
        {
            return m_body1.Active && m_body2.Active;
        }

        internal static Joint Create(JointDef def)
        {
	        Joint joint = null;

	        switch (def.type)
	        {
	            case JointType.DISTANCE:
		            joint = new DistanceJoint((DistanceJointDef)def);
		            break;

                //case b2JointType.e_mouseJoint:
                //    throw new NotImplementedException();
                //    //joint = new b2MouseJoint((b2MouseJointDef)def);
                //    break;

	            case JointType.PRISMATIC:
                    joint = new PrismaticJoint((PrismaticJointDef)def);
		            break;

	            case JointType.REVOLUTE:
                    joint = new RevoluteJoint((RevoluteJointDef)def);
		            break;

	            case JointType.PULLEY:
                    joint = new PulleyJoint((PulleyJointDef)def);
		            break;

	            case JointType.GEAR:
                    joint = new GearJoint((GearJointDef)def);
		            break;

	            case JointType.LINE:
                    joint = new LineJoint((LineJointDef)def);
		            break;

                case JointType.WELD:
                    joint = new WeldJoint((WeldJointDef)def);
                    break;

                case JointType.FRICTION:
                    joint = new FrictionJoint((FrictionJointDef)def);
                    break;

                case JointType.ROPE:
                    joint = new RopeJoint((RopeJointDef)def);
                    break;

	            default:
		            System.Diagnostics.Debug.Assert(false);
		            break;
	        }

	        return joint;
        }

        internal static void Destroy(Joint joint)
        {
            joint = null;
        }

        protected Joint(JointDef def)
        {
            System.Diagnostics.Debug.Assert(def.body1 != def.body2);

            m_type = def.type;
            m_prev = null;
            m_next = null;
            m_body1 = def.body1;
            m_body2 = def.body2;
            m_collideConnected = def.collideConnected;
            m_islandFlag = false;
            m_userData = def.userData;

            m_edgeA = new JointEdge();
            m_edgeA.joint = null;
            m_edgeA.other = null;
            m_edgeA.prev = null;
            m_edgeA.next = null;

            m_edgeB = new JointEdge();
            m_edgeB.joint = null;
            m_edgeB.other = null;
            m_edgeB.prev = null;
            m_edgeB.next = null;
        }

        internal abstract void InitVelocityConstraints(TimeStep step);
        internal abstract void SolveVelocityConstraints(TimeStep step);

	    // This returns true if the position errors are within tolerance.
	    internal abstract bool SolvePositionConstraints(float baumgarte);

        internal JointType m_type;
        internal Joint m_prev;
        internal Joint m_next;
        internal JointEdge m_edgeA;
        internal JointEdge m_edgeB;
        internal Body m_body1;
        internal Body m_body2;

        internal bool m_islandFlag;
        internal bool m_collideConnected;

        protected object m_userData;

	    // Cache here per time step to reduce cache misses.
        // TODO_ERIN will be wrong if the mass changes.
        protected Vector2 m_localCenter1, m_localCenter2;
        protected float m_invMass1, m_invI1;
        protected float m_invMass2, m_invI2;
    };

}
